home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 6 code / TCP / NewsWatcher / NW Source / Shared Code / Reusable Source / ftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-08  |  12.9 KB  |  474 lines  |  [TEXT/MMCC]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     ftp.c
  4.  
  5.     This reusable and reentrant module implements FTP operations.
  6.     
  7.     The following functions are exported:
  8.     
  9.         FtpOpen - Open an FTP stream.
  10.         FtpClose - Close an FTP stream.
  11.         FtpGetFile - Get a file.
  12.         FtpGetListing - Get a directory listing.
  13.         FtpPutFile - Put a file.
  14.         FtpDeleteFile - Delete a file.
  15.         FtpGetServerErrInfo - Get server error information.
  16.         FtpNumBytesTransferred - Get number of bytes transferred so far.
  17.     
  18.     You must call memutil.c/InitMemUtil and net.c/NetInit before calling any of
  19.     the functions in this module. You also must call the NetIdle function in your
  20.     idle loop, and the NetTerm function at program termination.
  21.         
  22.     A "stream" is an abstraction representing a bidirectional network connection
  23.     to an FTP server. A stream is represented as a variable of type "FtpStreamRef". 
  24.     These stream references are opaque. You may copy them and pass them as parameters 
  25.     to functions in ftp.c, but you are prohibited from accessing the contents of
  26.     the memory blocks pointed to by the references. Only the functions in 
  27.     ftp.c are permitted to manipulate the contents of these blocks.
  28.     
  29.     The functions return a value of type OSErr as the function result:
  30.     
  31.         noErr                no error occurred
  32.         ftpServerErr        server error
  33.         other                any other OS or Toolbox error code
  34.         
  35.     If the function result is ftpServerErr, the FtpGetServerErrInfo function can
  36.     be called to get information about the server error. On server errors, the 
  37.     stream is still allocated and open on return to the caller.
  38.         
  39.     If an OS or Toolbox error occurs, the stream is aborted and deallocated before
  40.     returning to the caller. "Aborted" means that the server connection is closed 
  41.     abruptly, without going through the usual TCP stream teardown process. You must 
  42.     perform careful error checking. The stream is deallocated, and must not be reused.
  43.     
  44.     Copyright © 1994-1995, Northwestern University.
  45.  
  46. ----------------------------------------------------------------------------*/
  47.  
  48. #include <stdlib.h>
  49. #include <string.h>
  50. #include <stdio.h>
  51.  
  52. #include "def.h"
  53. #include "net.h"
  54. #include "ftp.h"
  55. #include "memutil.h"
  56.  
  57.  
  58.  
  59. typedef struct TStream {
  60.     NetStreamRef netStream;                    /* net stream reference for control stream */
  61.     unsigned long addr;                        /* address of server */
  62.     Boolean textMode;                        /* true if stream currently in text mode */
  63.     Boolean passive;                        /* true to use passive mode */
  64.     NetStreamRef dataStream;                /* net stream reference for data stream, or
  65.                                                nil if no data stream open */
  66.     Boolean get;                            /* true if get, false if put, undefined if
  67.                                                no data stream open */
  68. } TStream, *TStreamPtr, **TStreamHandle;
  69.  
  70.  
  71.  
  72. /*----------------------------------------------------------------------------
  73.     FtpOpen 
  74.     
  75.     Open an FTP stream.
  76.     
  77.     Entry:    host = server host address (domain name or dotted 
  78.                 decimal IP address).
  79.             username = user name.
  80.             password = password.
  81.             passive = true to use passive mode FTP (recommended
  82.                 if more than one file is to be transferred).
  83.     
  84.     Exit:    function result = result code.
  85.             stream = reference to opened stream.
  86.             
  87.     In passive mode, the PASV command is used on all data transfers to
  88.     tell the FTP server to open a passive listening data stream for the
  89.     transfer using a unique port number. The FTP server sends a response
  90.     to the PASV command telling the Mac the port number to use. The Mac
  91.     connects to this port to establish the data stream connection.
  92.     
  93.     In active mode, the Mac establishes the listening data stream for
  94.     the transfer and uses the PORT command to tell the FTP server the
  95.     port number. The FTP server connects to this port to establish
  96.     the data stream connection.
  97.     
  98.     When transferring more than one file using a single control stream,
  99.     passive mode is recommended. In active mode, with some kinds of
  100.     servers, there can sometimes be significant delays (30-90 seconds) 
  101.     in establishing the data stream connection.
  102. ----------------------------------------------------------------------------*/
  103.  
  104. OSErr FtpOpen (char *host, char *username, char *password, Boolean passive,
  105.     FtpStreamRef *stream)
  106. {
  107.     TStreamHandle s = nil;
  108.     unsigned long addr;
  109.     unsigned short port;
  110.     NetStreamRef netStream;
  111.     CStr255 command, response;
  112.     long responseCode;
  113.     OSErr err = noErr;
  114.     
  115.     err = MyNewHandle(sizeof(TStream), &s);
  116.     if (err != noErr) return err;
  117.  
  118.     err = NetNameToAddr(host, kFTPPort, &addr, &port);
  119.     if (err != noErr) goto exit;
  120.     
  121.     err = NetOpen(addr, port, true, &netStream, &responseCode, response);
  122.     if (err != noErr) goto exit;
  123.  
  124.     (**s).netStream = netStream;
  125.     (**s).addr = addr;
  126.     (**s).textMode = true;
  127.     (**s).passive = passive;
  128.     *stream = (FtpStreamRef)s;
  129.     
  130.     if (responseCode != 220) return ftpServerErr;
  131.         
  132.     sprintf(command, "USER %.250s", username);
  133.     err = NetCommand(netStream, command, &responseCode, response);
  134.     if (err != noErr) goto exit;
  135.     if (responseCode != 331) return ftpServerErr;
  136.     
  137.     sprintf(command, "PASS %.250s", password);
  138.     err = NetCommand(netStream, command, &responseCode, response);
  139.     if (err != noErr) goto exit;
  140.     if (responseCode != 230) return ftpServerErr;
  141.     
  142.     return noErr;
  143.     
  144. exit:
  145.  
  146.     MyDisposeHandle(s);
  147.     return err;
  148. }
  149.  
  150.  
  151.  
  152. /*----------------------------------------------------------------------------
  153.     FtpClose 
  154.     
  155.     Close an FTP stream.
  156.     
  157.     Entry:    stream = stream reference.
  158.     
  159.     Exit:    function result = result code.
  160. ----------------------------------------------------------------------------*/
  161.  
  162. OSErr FtpClose (FtpStreamRef stream)
  163. {
  164.     TStreamHandle s;
  165.     OSErr err = noErr;
  166.     
  167.     s = (TStreamHandle)stream;
  168.     err = NetClose((**s).netStream);
  169.     MyDisposeHandle(s);
  170.     return err;
  171. }
  172.  
  173.  
  174.  
  175. /*----------------------------------------------------------------------------
  176.     GetOrPutFileOrListing
  177.     
  178.     Get or put a file or a listing from or to an FTP server.
  179.     
  180.     Entry:    stream = stream reference.
  181.             path = file path.
  182.             cmd = "RETR", "LIST", or "STOR".
  183.             get = true if get operation, false if put operation.
  184.             textFile = true if text file, false if binary file. Must be true
  185.                 for get listing operations.
  186.             data = pointer to handle. For put operations, the handle
  187.                 contains the data to put.
  188.     
  189.     Exit:    function result = result code.
  190.             *data = For get operations, contains the data.
  191. ----------------------------------------------------------------------------*/
  192.  
  193. static OSErr GetOrPutFileOrListing (FtpStreamRef stream, char *path, char *cmd,
  194.     Boolean get, Boolean textFile, Handle *data)
  195. {
  196.     TStreamHandle s;
  197.     NetStreamRef controlStream, dataStream;
  198.     unsigned long myAddr;
  199.     unsigned short port;
  200.     CStr255 command, response;
  201.     long responseCode;
  202.     OSErr err = noErr;
  203.     char *p;
  204.     short commaCount;
  205.     
  206.     s = (TStreamHandle)stream;
  207.     controlStream = (**s).netStream;
  208.     
  209.     if (textFile != (**s).textMode) {
  210.         err = NetCommand(controlStream, 
  211.             textFile ? "TYPE A" : "TYPE I",
  212.             &responseCode, response);
  213.         if (err != noErr) goto exit1;
  214.         if (responseCode != 200) return ftpServerErr;
  215.         (**s).textMode = textFile;
  216.     }
  217.     
  218.     if ((**s).passive) {
  219.     
  220.         /* Passive mode: Send PASV command. */
  221.         
  222.         err = NetCommand(controlStream, "PASV", &responseCode, response);
  223.         if (err != noErr) goto exit1;
  224.         if (responseCode != 227) return ftpServerErr;
  225.         
  226.         /* Parse the port number from the response to the PASV command:
  227.            227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
  228.  
  229.         p = response;
  230.         while (*p != 0 && *p != ')') p++;
  231.         if (*p == 0) return ftpServerErr;
  232.         commaCount = 0;
  233.         while (true) {
  234.             while (p > response && *p != ',') p--;
  235.             if (p <= response) return ftpServerErr;
  236.             commaCount++;
  237.             if (commaCount == 2) break;
  238.             p--;
  239.         }
  240.         p++;
  241.         port = atoi(p);
  242.         while (*p != ',') p++;
  243.         p++;
  244.         port = (port << 8) | atoi(p);
  245.         
  246.         /* Connect to server's listening data stream port. */
  247.         
  248.         err = NetOpen((**s).addr, port, false, &dataStream, &responseCode, response);
  249.         if (err != noErr) goto exit1;
  250.         
  251.     } else {
  252.     
  253.         /* Active mode: Establish listening data stream and send PORT command. */
  254.     
  255.         err = NetGetMyAddr(&myAddr);
  256.         if (err != noErr) goto exit1;
  257.     
  258.         err = NetFTPDataPassiveOpen(&port, &dataStream);
  259.         if (err != noErr) goto exit1;
  260.         
  261.         sprintf(command, "PORT %hu,%hu,%hu,%hu,%hu,%hu",
  262.             (unsigned short)((myAddr>>24)&0xff), 
  263.             (unsigned short)((myAddr>>16)&0xff), 
  264.             (unsigned short)((myAddr>>8)&0xff), 
  265.             (unsigned short)(myAddr&0xff),
  266.             (unsigned short)((port>>8)&0xff), 
  267.             (unsigned short)(port&0xff));
  268.         err = NetCommand(controlStream, command, &responseCode, response);
  269.         if (err != noErr) goto exit2; 
  270.         if (responseCode != 200) goto exit3;
  271.         
  272.     }
  273.  
  274.     (**s).dataStream = dataStream;
  275.     (**s).get = get;
  276.     
  277.     sprintf(command, "%s %.250s", cmd, path);
  278.     err = NetCommand(controlStream, command, &responseCode, response);
  279.     if (err != noErr) goto exit2;
  280.     if (responseCode != 150 && responseCode != 125) goto exit3;
  281.     
  282.     if (!(**s).passive) {
  283.     
  284.         /* Active mode: Wait for server to open its end of the data stream. */
  285.     
  286.         err = NetFTPDataWaitForConnection(dataStream);
  287.         if (err != noErr) goto exit1;
  288.         
  289.     }
  290.     
  291.     if (get) {
  292.         err = NetGetFTPData(dataStream, textFile, data);
  293.     } else {
  294.         err = NetPutFTPData(dataStream, textFile, *data);
  295.     }
  296.     if (err != noErr) goto exit1;
  297.     
  298.     (**s).dataStream = nil;
  299.     
  300.     err = NetFTPDataClose(dataStream);
  301.     if (err != noErr) goto exit1;
  302.     
  303.     err = NetGetExtraResponse(controlStream, &responseCode, response);
  304.     if (err != noErr) goto exit4;
  305.     if (responseCode != 226) return ftpServerErr;
  306.     
  307.     return noErr;
  308.     
  309. exit1:
  310.  
  311.     NetClose(controlStream);
  312.     MyDisposeHandle(s);
  313.     return err;
  314.     
  315. exit2:
  316.  
  317.     NetFTPDataClose(dataStream);
  318.     MyDisposeHandle(s);
  319.     return err;
  320.     
  321. exit3:
  322.  
  323.     NetFTPDataClose(dataStream);
  324.     (**s).dataStream = nil;
  325.     return ftpServerErr;
  326.     
  327. exit4:
  328.  
  329.     MyDisposeHandle(s);
  330.     return err;
  331. }
  332.  
  333.  
  334.  
  335. /*----------------------------------------------------------------------------
  336.     FtpGetFile 
  337.     
  338.     Get a file from an FTP server.
  339.     
  340.     Entry:    stream = stream reference.
  341.             path = file path.
  342.             textFile = true if text file, false if binary file.
  343.     
  344.     Exit:    function result = result code.
  345.             data = handle to data.
  346. ----------------------------------------------------------------------------*/
  347.  
  348. OSErr FtpGetFile (FtpStreamRef stream, char *path, Boolean textFile, Handle *data)
  349. {
  350.     return GetOrPutFileOrListing(stream, path, "RETR", true, textFile, data);
  351. }
  352.  
  353.  
  354.  
  355. /*----------------------------------------------------------------------------
  356.     FtpGetListing 
  357.     
  358.     Get a directory listing from an FTP server.
  359.     
  360.     Entry:    stream = stream reference.
  361.             path = directory or file path.
  362.     
  363.     Exit:    function result = result code.
  364.             data = handle to listing.
  365. ----------------------------------------------------------------------------*/
  366.  
  367. OSErr FtpGetListing (FtpStreamRef stream, char *path, Handle *data)
  368. {
  369.     return GetOrPutFileOrListing(stream, path, "LIST", true, true, data);
  370. }
  371.  
  372.  
  373.  
  374. /*----------------------------------------------------------------------------
  375.     FtpPutFile 
  376.     
  377.     Send a file to an FTP server.
  378.     
  379.     Entry:    stream = stream reference.
  380.             path = file path.
  381.             textFile = true if text file, false if binary file.
  382.             data = handle to data.
  383.     
  384.     Exit:    function result = result code.
  385. ----------------------------------------------------------------------------*/
  386.  
  387. OSErr FtpPutFile (FtpStreamRef stream, char *path, Boolean textFile, Handle data)
  388. {
  389.     return GetOrPutFileOrListing(stream, path, "STOR", false, textFile, &data);
  390. }
  391.  
  392.  
  393.  
  394. /*----------------------------------------------------------------------------
  395.     FtpDeleteFile 
  396.     
  397.     Delete a file on an FTP server.
  398.     
  399.     Entry:    stream = stream reference.
  400.             path = file path.
  401.     
  402.     Exit:    function result = result code.
  403. ----------------------------------------------------------------------------*/
  404.  
  405. OSErr FtpDeleteFile (FtpStreamRef stream, char *path)
  406. {
  407.     TStreamHandle s;
  408.     NetStreamRef controlStream;
  409.     CStr255 command, response;
  410.     long responseCode;
  411.     OSErr err = noErr;
  412.     
  413.     s = (TStreamHandle)stream;
  414.     controlStream = (**s).netStream;
  415.     
  416.     sprintf(command, "DELE %.250s", path);
  417.     err = NetCommand(controlStream, command, &responseCode, response);
  418.     if (err != noErr) goto exit;
  419.     if (responseCode != 250) return ftpServerErr;
  420.     
  421.     return noErr;
  422.     
  423. exit:
  424.  
  425.     MyDisposeHandle(s);
  426.     return err;
  427. }
  428.  
  429.  
  430.  
  431. /*----------------------------------------------------------------------------
  432.     FtpGetServerErrInfo 
  433.     
  434.     Get server error information.
  435.     
  436.     Entry:    stream = stream reference.
  437.     
  438.     Exit:    *serverErrInfo = server error information.
  439. ----------------------------------------------------------------------------*/
  440.  
  441. void FtpGetServerErrInfo (FtpStreamRef stream, NetServerErrInfo *serverErrInfo)
  442. {
  443.     TStreamHandle s;
  444.     
  445.     s = (TStreamHandle)stream;
  446.     NetGetServerErrInfo((**s).netStream, serverErrInfo);
  447. }
  448.  
  449.  
  450.  
  451. /*----------------------------------------------------------------------------
  452.     FtpNumBytesTransferred 
  453.     
  454.     Get the number of data bytes transferred so far.
  455.     
  456.     Entry:    stream = stream reference.
  457.     
  458.     Exit:    function result = number of bytes transferred, or 0 if no data
  459.                 stream open.
  460. ----------------------------------------------------------------------------*/
  461.  
  462. long FtpNumBytesTransferred (FtpStreamRef stream)
  463. {
  464.     TStreamHandle s;
  465.     NetStreamRef dataStream;
  466.     long bytesIn, bytesOut;
  467.     
  468.     s = (TStreamHandle)stream;
  469.     dataStream = (**s).dataStream;
  470.     if (dataStream == nil) return 0;
  471.     NetGetStreamStats(dataStream, &bytesIn, &bytesOut);
  472.     return (**s).get ? bytesIn : bytesOut;
  473. }
  474.